package cn.demoncat.util.ocr.huawei;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.exception.ConnRuntimeException;
import cn.demoncat.util.io.ImgUtil;
import cn.demoncat.util.io.IoUtil;
import cn.demoncat.util.lang.JsonUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StatusConstant;
import cn.demoncat.util.lang.entity.MapSs;
import cn.demoncat.util.lang.entity.card.BankCard;
import cn.demoncat.util.lang.entity.card.BusinessLicense;
import cn.demoncat.util.lang.entity.card.IdCard;
import cn.demoncat.util.ocr.huawei.config.HwOcrProperties;
import cn.demoncat.util.ocr.huawei.constant.HwOcrApi;
import cn.demoncat.util.ocr.huawei.entity.BankCardItem;
import cn.demoncat.util.ocr.huawei.entity.BusinessLicenseItem;
import cn.demoncat.util.ocr.huawei.entity.IdCardItem;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.huawei.ais.common.AuthInfo;
import com.huawei.ais.sdk.AisAccess;
import com.huawei.ais.sdk.util.HttpClientUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.entity.StringEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 图像识别：华为 OCR
 *
 * @author 延晓磊
 * @since 2020年10月23日
 */
@Component
public class HwOcrUtil {

    static {
        // HTTP连接配置
        HttpClientUtils.DEFAULT_CONNECTION_TIMEOUT = 15000;
        HttpClientUtils.DEFAULT_CONNECTION_REQUEST_TIMEOUT = 15000;
        HttpClientUtils.DEFAULT_SOCKET_TIMEOUT = 15000;
    }

    // 图片限制 10M
    private static final long FILE_MAX = 10 * 1024 * 1024;

    // 华为云-识别服务
    private final AisAccess aisService;

    private static final Logger LOG = LoggerFactory.getLogger(HwOcrUtil.class);

    @Autowired
    public HwOcrUtil(HwOcrProperties hwOcrProperties) {
        this.aisService = new AisAccess(new AuthInfo("https://ocr." + hwOcrProperties.getRegion() + ".myhuaweicloud.com", hwOcrProperties.getRegion(), hwOcrProperties.getAccessKey(), hwOcrProperties.getAccessPassword()));
    }


    /**
     * 身份证识别
     *
     * @param file 图片路径/URL：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     * @param side 1正面，0反面，""/null自动识别（准确率稍低）
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public IdCard idCard(String file, String side) {
        return response(HwOcrApi.ID_CARD, ocr(HwOcrApi.ID_CARD, file, StringUtils.isBlank(side) ? null : MapSs.getInstance("side", StatusConstant.Common.isYes(side) ? "front" : "back")), IdCardItem.class).toCommonData();
    }

    /**
     * 身份证识别
     *
     * @param file 图片：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     * @param side 1正面，0反面，""/null自动识别（准确率稍低）
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public IdCard idCard(byte[] file, String side) {
        return response(HwOcrApi.ID_CARD, ocr(HwOcrApi.ID_CARD, file, StringUtils.isBlank(side) ? null : MapSs.getInstance("side", StatusConstant.Common.isYes(side) ? "front" : "back")), IdCardItem.class).toCommonData();
    }

    /**
     * 银行卡识别
     *
     * @param file 图片路径/URL：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public BankCard bankCard(String file) {
        return response(HwOcrApi.BANK_CARD, ocr(HwOcrApi.BANK_CARD, file, null), BankCardItem.class).toCommonData();
    }

    /**
     * 银行卡识别
     *
     * @param file 图片：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public BankCard bankCard(byte[] file) {
        return response(HwOcrApi.BANK_CARD, ocr(HwOcrApi.BANK_CARD, file, null), BankCardItem.class).toCommonData();
    }

    /**
     * 营业执照识别
     *
     * @param file 图片路径/URL：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public BusinessLicense businessLicense(String file) {
        return response(HwOcrApi.BUSINESS_LICENSE, ocr(HwOcrApi.BUSINESS_LICENSE, file, null), BusinessLicenseItem.class).toCommonData();
    }

    /**
     * 营业执照识别
     *
     * @param file 图片：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public BusinessLicense businessLicense(byte[] file) {
        return response(HwOcrApi.BUSINESS_LICENSE, ocr(HwOcrApi.BUSINESS_LICENSE, file, null), BusinessLicenseItem.class).toCommonData();
    }

    /**
     * 图片识别
     *
     * @param api    服务接口
     * @param file   图片路径/URL：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     * @param params 请求参数
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public String ocr(HwOcrApi api, String file, Map<String, String> params) {
        if (file.startsWith("http://") || file.startsWith("https://")) {
            // 远程图片
            if (params == null) {
                params = new HashMap<>();
            }
            params.put("url", file);
            // 发起请求
            return request(api, params);
        } else {
            // 本地图片
            return ocr(api, IoUtil.readToBytes(file), params);
        }
    }

    /**
     * 图片识别
     *
     * @param api    服务接口
     * @param file   图片：限制10MB以下，支持JPEG、JPG、PNG、BMP、TIFF
     * @param params 请求参数
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    public String ocr(HwOcrApi api, byte[] file, Map<String, String> params) {
        // 图片压缩
        if (file.length > FILE_MAX) {
            file = ImgUtil.zoom(IoUtil.getInputStream(file), FILE_MAX);
        }
        if (params == null) {
            params = new HashMap<>();
        }
        // BASE64编码
        params.put("image", Base64.encodeBase64String(file));
        // 发起请求
        return request(api, params);
    }

    /**
     * 发起请求
     *
     * @param api    接口
     * @param params 参数
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    private String request(HwOcrApi api, Map<String, String> params) {
        // 发起请求
        HttpResponse response = aisService.post(api.getUrl(), new StringEntity(JsonUtil.toStr(params), StandardCharsets.UTF_8));
        if (response == null) {
            throw new ConnRuntimeException(StringUtil.logFormat("请求华为云OCR失败：api = {}, url = {}, response = ", api.getName(), api.getUrl(), null));
        }
        if (response.getStatusLine().getStatusCode() != 200) {
            throw new ConnRuntimeException(StringUtil.logFormat("请求华为云OCR失败：api = {}, url = {}, status = {}, reason = {}", api.getName(), api.getUrl(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()));
        }
        // 读取结果
        try {
            return IoUtil.readToStr(response.getEntity().getContent());
        } catch (IOException e) {
            throw new ConnRuntimeException(StringUtil.logFormat("请求华为云OCR失败：api = {}, url = {}, status = {}", api.getName(), api.getUrl(), response.getStatusLine().getStatusCode()), e);
        }
    }

    /**
     * 结果转换
     *
     * @param api
     * @param response 请求结果
     * @param clazz
     * @param <T>
     *
     * @return
     *
     * @author 延晓磊
     * @since 2020年10月23日
     */
    private <T> T response(HwOcrApi api, String response, Class<T> clazz) {
        JSONObject obj = JSON.parseObject(response);
        // 响应码
        String code = obj.getString("error_code");
        if (StringUtils.isBlank(code) || code.endsWith("0000")) {
            // 成功
            return JsonUtil.parse(obj.getString("result"), clazz);
        } else {
            // 失败
            LOG.warn("请求华为云OCR错误：api = {}, url = {}, response = {}", api.getName(), api.getUrl(), response);
            throw new BizRuntimeException(obj.getString("error_msg"));
        }
    }
}
